/*
This is part of Screenbar, which is based in part on the Litestep
shell source code.

Copyright (C) 2008-2009 Jim Babcock
Copyright (C) 1997-2009 The LiteStep Development Team

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
#include "stdafx.h"

MinimapElement::MinimapElement(string prefix)
	:RectLayoutElement(prefix)
{
	// Minimap display settings
	minimapTitleBarsThickness = getConfigBool("MinimapTitleBars", true, prefix.c_str())
		? getConfigInt("MinimapTitleBarThickness", 4, prefix.c_str())
		: 0;
	minimapIconSize = getConfigInt("MinimapIconSize", 16, prefix.c_str());
	if(!getConfigBool("MinimapTitleBars", true, prefix.c_str()))
		minimapTitleBarsThickness = 0;
	if(!getConfigBool("MinimapIcons", true, prefix.c_str()))
		minimapIconSize = 0;
	
	minimapWindowColor          = getConfigColor("MinimapWinColor", 255, 255, 255, prefix.c_str());
	minimapTitleBarColor        = getConfigColor("MinimapTitleBarColor", 80, 80, 80, prefix.c_str());
	minimapFocusedTitleBarColor = getConfigColor("MinimapFocusedTitleBarColor", 0, 0, 255, prefix.c_str());
	minimapWindowBorderColor    = getConfigColor("MinimapWinBorderColor", 0, 0, 0, prefix.c_str());
}

MinimapElement::~MinimapElement()
{
}

/// px, py in minimap-space coordinates
WindowData *MinimapElement::getWindow(const LayoutCacheNode *node, int px, int py)
{
	POINT pt = {px, py};
	
	vector<WindowData*> windows;
	windowTracker->getWindows(node->context.desk, windows);
	
	for(vector<WindowData*>::iterator it = windows.begin(); it != windows.end(); it++)
	{
		WindowData *windowData = *it;
		RECT vwmRect = screenToMinimapPos(node, windowData->lastScreenPos);
		
		if(PtInRect(&vwmRect, pt))
			return windowData;
	}
	
	return NULL;
}

RECT MinimapElement::screenToMinimapPos(const LayoutCacheNode *node, Rect screenPos, POINT *iconPos)
{
	// Remove translation due to being on the second monitor, being offscreen,
	// etc.
	Rect maximizeArea;
	VirtualDesktop *desk = node->context.desk;
	
	if(desk->isVisible()) {
		Monitor *monitor = desk->getMonitor();
		maximizeArea = monitor->getMaximizeArea();
		monitor->unstoreRect(maximizeArea);
	} else {
		maximizeArea = desk->lastMaximizeArea;
	}
	
	desk->currentSlot()->unstoreRect(screenPos);
	
	// Promote to float for intermediate calculations;
	float left, right, top, bottom;
	left   = (float)screenPos.left - maximizeArea.left;
	right  = (float)screenPos.getRight() - maximizeArea.left;
	top    = (float)screenPos.top - maximizeArea.top;
	bottom = (float)screenPos.getBottom() - maximizeArea.top;
	
	RECT minimapRect = getRect(node->boundingRect);
	int minimapWidth = minimapRect.right - minimapRect.left;
	int minimapHeight = minimapRect.bottom - minimapRect.top;
	
	// Scale it to minimap size
	//  +1 and -2 because the minimap's borders don't count
	left   *= (float)(minimapWidth-2)  / maximizeArea.width;
	right  *= (float)(minimapWidth-2)  / maximizeArea.width;
	top    *= (float)(minimapHeight-2) / maximizeArea.height;
	bottom *= (float)(minimapHeight-2) / maximizeArea.height;
		left++;
		right++;
		top++;
		bottom++;
	
	if(left < 0)
		left = 0;
	if(top < 0)
		top = 0;
	if(right > minimapWidth)
		right = minimapWidth;
	if(bottom > minimapHeight)
		bottom = minimapHeight;
	if(left > right)
		left = right;
	if(top > bottom)
		top = bottom;
	
	if(iconPos)
	{
		iconPos->x = (left+right)/2 - minimapIconSize/2;
		iconPos->y = (top+bottom)/2 - minimapIconSize/2;
		
		if(iconPos->x + minimapIconSize > minimapWidth)
			iconPos->x = minimapWidth - minimapIconSize;
		if(iconPos->y + minimapIconSize > minimapHeight)
			iconPos->y = minimapHeight - minimapIconSize;
		if(iconPos->x < 0)
			iconPos->x = 0;
		if(iconPos->y < 0)
			iconPos->y = 0;
	}
	
	RECT ret;
		ret.left   = (long)left;
		ret.right  = (long)right;
		ret.top    = (long)top;
		ret.bottom = (long)bottom;
	return ret;
}

POINT MinimapElement::minimapToScreenPos(const LayoutCacheNode *node, int x, int y, int windowWidth, int windowHeight)
{
	POINT ret;
	RECT maximizeArea;
	VirtualDesktop *desk = node->context.desk;
	
	/*if(desk->monitor)
		maximizeArea = desk->monitor->getMaximizeArea();
	else
		maximizeArea = desk->lastMaximizeArea;*/
	maximizeArea = desk->currentSlot()->getBoundingRect();
	
	RECT minimapArea = getRect(node->boundingRect);
	
	ret.x = x * ((float)(maximizeArea.right-maximizeArea.left) / (minimapArea.right-minimapArea.left));
	ret.y = y * ((float)(maximizeArea.bottom-maximizeArea.top) / (minimapArea.bottom-minimapArea.top));
	ret.x += maximizeArea.left;
	ret.y += maximizeArea.top;
	
	if(ret.x + windowWidth > maximizeArea.right)
		ret.x = maximizeArea.right - windowWidth;
	if(ret.y + windowHeight > maximizeArea.bottom)
		ret.y = maximizeArea.bottom - windowHeight;
	if(ret.x < maximizeArea.left)
		ret.x = maximizeArea.left;
	if(ret.y < maximizeArea.top)
		ret.y = maximizeArea.top;
	
	return ret;
}

struct MinimapElementState
{
	int desk;
	int changeIndex;
};

bool MinimapElement::changed(const LayoutCacheNode *node)
{
	MinimapElementState *state = (MinimapElementState*)node->elementData;
	if(!state) return true;
	
	if(node->context.desk->getIndex() != state->desk)
		return true;
	else if(state->changeIndex != windowTracker->getMinimapChangeIndex(state->desk))
		return true;
	else
		return false;
}

void MinimapElement::update(LayoutCacheNode *node)
{
	MinimapElementState *state = (MinimapElementState*)node->elementData;
	if(!state) {
		state = new MinimapElementState();
		node->elementData = state;
	}
	
	state->desk = node->context.desk->getIndex();
	state->changeIndex = windowTracker->getMinimapChangeIndex(state->desk);
}

void MinimapElement::deleteElementData(LayoutCacheNode *node, void *elementData)
{
	MinimapElementState *state = (MinimapElementState*)node->elementData;
	if(state)
		delete state;
}


BangCommand(StartMinimapDrag)
{
	if(!clickContext) return;
	LayoutCacheNode *clickedNode = clickContext->getNode();
	if(!clickedNode) return;
	
	MinimapElement *minimap = dynamic_cast<MinimapElement*>(clickedNode->element);
	if(!minimap) return;
	
	minimap->beginDrag(clickedNode);
}

class MinimapDragItem
	:public DragItem
{
public:
	MinimapDragItem(LayoutReference *minimap, HWND window, Point offset,
		Rect origRect, VirtualDesktop *sourceDesk);
	HWND window;
	Point offset;
	Rect origRect;
	VirtualDesktop *sourceDesk;
};

MinimapDragItem::MinimapDragItem(LayoutReference *minimap, HWND window,
	Point offset, Rect origRect, VirtualDesktop *sourceDesk)
	:DragItem(minimap)
{
	this->window = window;
	this->offset = offset;
	this->origRect = origRect;
	this->sourceDesk = sourceDesk;
}

void MinimapElement::beginDrag(LayoutCacheNode *node)
{
	// Determine which window is being dragged
	Point sCursorPos = getCursorPos();
	
	// Convert to minimap-space coordinates
	Rect minimapRect = getRect(node->boundingRect);
	ScreenbarWindow *contextWindow = node->context.window;
	Point minimapTopLeft = minimapRect.topLeft() + contextWindow->getTopLeft();
	Point mCursorPos = sCursorPos - minimapTopLeft;
	
	WindowData *window = getWindow(node, mCursorPos.x, mCursorPos.y);
	if(!window)
		return;
	
	Rect windowPos = screenToMinimapPos(node, window->lastScreenPos, NULL);
	Point mouseOffset = mCursorPos - windowPos.topLeft();
	
	LayoutReference *ref = node->owner->getReferenceTo(node);
	MinimapDragItem *dragItem = new MinimapDragItem(ref, window->handle,
		mouseOffset, window->lastScreenPos, window->desk);
	delete ref;
	
	ElementContext dragContext = node->context;
	dragContext.taskWindow = window->handle;
	dragState.beginDrag(dragItem, dragContext);
}

void MinimapElement::hoverDragged(LayoutCacheNode *node, DragItem *item)
{
	MinimapDragItem *minimapDrag = dynamic_cast<MinimapDragItem*>(item);
	if(minimapDrag)
	{
		Rect minimapRect = getRect(node->boundingRect);
		Point minimapTopLeft = minimapRect.topLeft() + node->context.window->getTopLeft();
		Point cursorPos = getCursorPos() - minimapTopLeft;
		Point newWindowTopLeft;
		
		HWND window = minimapDrag->window;
		RECT windowRect;
		GetWindowRect(window, &windowRect);
		
		if(minimapDrag->sourceDesk == node->context.desk) {
			Point miniwindowTopLeft = cursorPos - minimapDrag->offset;
			newWindowTopLeft = minimapToScreenPos(node, miniwindowTopLeft.x, miniwindowTopLeft.y,
				windowRect.right-windowRect.left, windowRect.bottom-windowRect.top);
		} else {
			VirtualDesktop *targetDesk = node->context.desk;
			Rect rect = minimapDrag->origRect;
			targetDesk->currentSlot()->transferFrom(rect, minimapDrag->sourceDesk->currentSlot());
			newWindowTopLeft = rect.topLeft();
		}
		
		SetWindowPos(minimapDrag->window, NULL, newWindowTopLeft.x, newWindowTopLeft.y, 0, 0,
			SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
	}
	else
	{
		if(node->parent)
			node->parent->element->hoverDragged(node->parent, item);
	}
}
